home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / demos / audio / bz / bzsolo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  39.9 KB  |  1,679 lines

  1. /*
  2.  * Copyright (C) 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /***************************************************************************
  18.  *
  19.  * BZ - Multiplayer tank game -- solo module.
  20.  *
  21.  * $Id: bzsolo.c,v 1.3 1993/08/11 19:46:29 adele Exp $
  22.  *
  23.  *                    Chris Fouts - Silicon Graphics, Inc.
  24.  *                    April, 1992
  25.  **************************************************************************/
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <math.h>
  29. #include <string.h>
  30. #include <bstring.h>
  31. #include <sys/time.h>
  32. #include <sys/param.h>
  33. #include <X11/Intrinsic.h>
  34. #include <gl/gl.h>
  35. #include "bz.h"
  36. #include "bzobjs.h"
  37. #include "bzsolo.h"
  38. #include "bzbase.h"
  39. #include "bzcollide.h"
  40. #include "bzsound.h"
  41. #include "framerate.h"
  42.  
  43. #define    MISLRMIN            20.0f
  44. #define    MISSILE_STRIKE        1
  45. #define    VAPORIZE            2
  46. #define    FRIENDLY_FIRE        3
  47.  
  48. #define    REMINDER_DELAY        180.0f
  49.  
  50. static struct BzEnemy {
  51.     unsigned        avoiding ;
  52.     unsigned        lead_me ;
  53.     unsigned        flip ;
  54.     unsigned        can_wait ;
  55.     unsigned        turning ;
  56.     int                enemy_loading ;
  57.     int                mine_status ;
  58.     float            mine_x ;
  59.     float            mine_y ;
  60.     float            target_angle ;
  61.     float            turning_l ;
  62.     float            turning_r ;
  63.     float            dist ;
  64.     struct timeval    msl_launch ;
  65.     struct timeval    msl_explode ;
  66.     struct timeval    death_time ;
  67.     struct timeval    wait_time ;
  68.     } robot[MAX_ENEMY+1] ;
  69.  
  70.  
  71. static int        max_wait = 200 ;
  72. static float    zig_zag_angle = 0 ;
  73. extern float    vx[] ;
  74. extern float    vy[] ;
  75. extern float    msl_vx[] ;
  76. extern float    msl_vy[] ;
  77. extern float    last_msl_x[] ;
  78. extern float    last_msl_y[] ;
  79. static float    fire_ang = 30.0f ;
  80. static unsigned    tanks_per_level ;
  81. static unsigned    super_tank_prob ;
  82. static unsigned    runner_tank_prob ;
  83. static unsigned    new_man_score ;
  84. static unsigned new_man_score_inc ;
  85.  
  86. extern unsigned int        number_players ;
  87. extern struct BzMember    player[] ;
  88. extern float            obst_x[] ;
  89. extern float            obst_y[] ;
  90. extern struct timeval    current_time ;
  91. extern float            rm_vel ;
  92. extern float            d_time ;
  93. extern Boolean            need_to_show_score ;
  94. extern Boolean            need_to_show_lives ;
  95. extern unsigned int        lives ;
  96. extern unsigned int        practice_level ;
  97. extern float            vx[] ;
  98. extern float            vy[] ;
  99. extern float            vmax[] ;
  100. extern float            t_factor ;
  101. extern long                loading_missile ;
  102. extern unsigned int        level ;
  103. extern Boolean            need_to_show_players ;
  104. extern Boolean            need_to_show_level ;
  105. extern GL_Object        tank_logo[] ;
  106. extern int                tank_logo_side[] ;
  107. extern char                player_name[] ;
  108. extern struct timeval    missile_launch_time ;
  109. extern struct timeval    missile_explode_time ;
  110. extern struct timeval    time_of_death ;
  111. extern struct timeval    hit_time ;
  112. extern Boolean            kids_mode ;
  113.  
  114.  
  115.  
  116. static struct timeval    pause_begin ;
  117. static struct timeval    pause_end ;
  118. static long                reminders ;
  119.  
  120. static long            logo_count = 0 ;
  121. static GL_Object    *robot_logo = NULL ;
  122. static int            *robot_logo_side = NULL ;
  123.  
  124. /* BEGIN PROTOTYPES -S bzsolo.c */
  125. static int          aimed_at_enemy( unsigned int enemy ) ;
  126. static void         avoid_missile( unsigned int enemy, float v, float dxm,
  127.                         float dym, float ang, float dang, float dist,
  128.                         float *l, float *r ) ;
  129. static void         bury_enemy( unsigned int enemy ) ;
  130. static void         check_enemy_hit( unsigned int enemy ) ;
  131. static int          clear_shot( unsigned int enemy ) ;
  132. static void         do_top_10( void ) ;
  133. static void         enemy_fire( unsigned int enemy ) ;
  134. static void         enemy_mine( unsigned int enemy ) ;
  135. static void         enemy_move( unsigned int enemy ) ;
  136. static void         explode_enemy( unsigned int enemy, int kill_type ) ;
  137. static char        *get_topscore_file( void ) ;
  138. static void         inc_level( void ) ;
  139. static void         list_top_10( void ) ;
  140. static int          load_top_10_stuff( FILE *f, char names[][NAMELEN],
  141.                         unsigned int topscore[], int max_scores ) ;
  142. static GL_Object    next_logo( int *logo_side ) ;
  143. static char        *next_name( int i, char *names[] ) ;
  144. static char        *next_regular_name( void ) ;
  145. static char        *next_runner_name( void ) ;
  146. static char        *next_super_name( void ) ;
  147. static void         place_enemy( int i ) ;
  148. static void         prowl( unsigned int enemy, float dang, unsigned seen,
  149.                         float dist, float *l, float *r ) ;
  150. static void         robot_logo_init( void ) ;
  151. /* END PROTOTYPES -S bzsolo.c */
  152.  
  153.  
  154.  
  155. /*------------------------------------------------------------------------------
  156.  * Do all stuff for solo mode.
  157.  *----------------------------------------------------------------------------*/
  158. void do_enemy_stuff( void )
  159. {
  160.     int                i ;
  161.     int                side ;
  162.     unsigned int    hit_tank ;
  163.     unsigned int    obst ;
  164.     long            status ;
  165.  
  166.     for( i = ENEMY ; i < number_players ; i++ ) {
  167.  
  168.         if( IS_ALIVE( player[i] ) ) {
  169.  
  170.             enemy_move( i ) ;
  171.  
  172.             player[i].x += vx[i] * d_time ;
  173.             player[i].y += vy[i] * d_time ;
  174.  
  175.             switch( check_tank_collision( i, &obst ) ) {
  176.                 case 1 :
  177.                     move_after_hit( i, player[obst].x, player[obst].y,
  178.                             vx[i], vy[i] ) ;
  179.                     break ;
  180.  
  181.                 case 2 :
  182.                     move_after_hit( i, obst_x[obst], obst_y[obst],
  183.                             vx[i], vy[i] ) ;
  184.                     break ;
  185.  
  186.                 default :
  187.                 case 0 :
  188.                     break ;
  189.                 }
  190.  
  191.             robot[i].dist = hypotn( player[SELF].x - player[i].x,
  192.                                     player[SELF].y - player[i].y ) ;
  193.  
  194.             if( robot[i].enemy_loading ) {
  195.                 status = RM_DURATN - elapsed_sec_tenths( &(robot[i].msl_launch),
  196.                                                          ¤t_time ) ;
  197.                 if( status <= 0 ) {
  198.                     robot[i].enemy_loading = 0 ;
  199.                     }
  200.                 else {
  201.                     robot[i].enemy_loading = status ;
  202.                     }
  203.                 }
  204.  
  205.             /*
  206.              * Mine support.
  207.              */
  208.             if( IS_ALIVE( player[SELF] ) ) {
  209.                 enemy_mine( i ) ;
  210.                 }
  211.             }
  212.         else {
  213.             status = MAX_DEAD_STATUS -
  214.                  elapsed_sec_tenths( &(robot[i].death_time), ¤t_time ) ;
  215.             if( status < MIN_DEAD_STATUS + 20 ) {
  216.                 player[i].status = -1 ;
  217.                 }
  218.             else {
  219.                 player[i].status = status ;
  220.                 }
  221.             }
  222.  
  223.         if( player[i].msl_status > 0 ) {
  224.             status = RM_DURATN -
  225.                  elapsed_sec_tenths( &(robot[i].msl_launch), ¤t_time ) ;
  226.             if( status > 0 ) {
  227.                 player[i].msl_status = status ;
  228.                 last_msl_x[i] = player[i].msl_x ;
  229.                 last_msl_y[i] = player[i].msl_y ;
  230.                 player[i].msl_x += msl_vx[i] * d_time ;
  231.                 player[i].msl_y += msl_vy[i] * d_time ;
  232.                 switch( check_missile_hit( i, &hit_tank, &side ) ) {
  233.  
  234.                     case 1 :
  235.                         status = -1 ;
  236.                         player[i].msl_x = player[hit_tank].x ;
  237.                         player[i].msl_y = player[hit_tank].y ;
  238.                         msl_vx[i] = msl_vy[i] = 0.f ;
  239.                         player[i].hit_type = HIT_BY_MISSILE ;
  240.                         if( hit_tank == SELF ) {
  241.                             blow_up_self( i ) ;
  242.                             }
  243.                         else {
  244.                             explode_enemy( hit_tank, FRIENDLY_FIRE ) ;
  245.                             post_new_message( 0, 0, "%s killed %s.",
  246.                                 player[i].name, ( i == hit_tank ) ?
  247.                                 "self" : player[hit_tank].name ) ;
  248.                             }
  249.                         break ;
  250.  
  251.                     case 2 :
  252.                         player[i].msl_x -= msl_vx[i] * d_time ;
  253.                         player[i].msl_y -= msl_vy[i] * d_time ;
  254.                         switch( side ) {
  255.                             case MIN_X_SIDE :
  256.                             case MAX_X_SIDE :
  257.                                 msl_vx[i] = -msl_vx[i] ;
  258.                                 break ;
  259.                             case MIN_Y_SIDE :
  260.                             case MAX_Y_SIDE :
  261.                                 msl_vy[i] = -msl_vy[i] ;
  262.                                 break ;
  263.                             }
  264.                         player[i].msl_head = getangle( msl_vy[i], msl_vx[i] ) ;
  265.                         break ;
  266.  
  267.                     default :
  268.                         if( player[i].msl_x < -FIELD_SIZE ) {
  269.                             player[i].msl_x = -FIELD_SIZE ;
  270.                             status = -1 ;
  271.                             }
  272.                         if( player[i].msl_x >  FIELD_SIZE ) {
  273.                             player[i].msl_x =  FIELD_SIZE ;
  274.                             status = -1 ;
  275.                             }
  276.                         if( player[i].msl_y < -FIELD_SIZE ) {
  277.                             player[i].msl_y = -FIELD_SIZE ;
  278.                             status = -1 ;
  279.                             }
  280.                         if( player[i].msl_y >  FIELD_SIZE ) {
  281.                             player[i].msl_y =  FIELD_SIZE ;
  282.                             status = -1 ;
  283.                             }
  284.                         break ;
  285.  
  286.                     }
  287.                 }
  288.             if( status <= 0 ) {
  289.                 player[i].msl_status = -MEXPL_DURATION ;
  290.                 copy_time( &(robot[i].msl_explode), ¤t_time ) ;
  291.                 explosion_sfx( player[i].msl_x - player[SELF].x,
  292.                                player[i].msl_y - player[SELF].y,
  293.                                player[SELF].head ) ;
  294.                 }
  295.             }
  296.         else if( player[i].msl_status < 0 ) {
  297.             status = -MEXPL_DURATION +
  298.                      elapsed_sec_tenths( &(robot[i].msl_explode),
  299.                                          ¤t_time ) ;
  300.             if( status >= 0 ) {
  301.                 player[i].msl_status = 0 ;
  302.                 }
  303.             else {
  304.                 player[i].msl_status = status ;
  305.                 }
  306.             }
  307.  
  308.         check_enemy_hit( i ) ;
  309.         }
  310.  
  311.     /*
  312.      * Clean up after any dead players.
  313.      */
  314.     for( i = number_players - 1 ; i > SELF ; i-- ) {
  315.         if( player[i].status == -1 ) {
  316.             bury_enemy( i ) ;
  317.             }
  318.         }
  319. }
  320.  
  321.  
  322.  
  323. /*------------------------------------------------------------------------------
  324.  * Enemy mine support.
  325.  *----------------------------------------------------------------------------*/
  326. static void enemy_mine(
  327.     unsigned int enemy
  328.     )
  329. {
  330.     float    dx ;
  331.     float    dy ;
  332.     float    d2 ;
  333.  
  334.     /*
  335.      * If mine has been dropped, check to explode it.
  336.      */
  337.     if( robot[enemy].mine_status ) {
  338.         dx = player[SELF].x - robot[enemy].mine_x ;
  339.         dy = player[SELF].y - robot[enemy].mine_y ;
  340.         d2 = dx * dx + dy * dy ;
  341.         /*
  342.          * Detonate mine if tank is within range.
  343.          */
  344.         if( d2 < DET_MINE_RADIUS * DET_MINE_RADIUS ) {
  345.             player[enemy].hit_type = HIT_BY_MINE ;
  346.             robot[enemy].mine_status = 0 ;
  347.             blow_up_self( enemy ) ;
  348.             }
  349.         /*
  350.          * Detonate mine if SELF is much farther from mine than from enemy.
  351.          */
  352.         else if( d2 > 2.0f * robot[enemy].dist * robot[enemy].dist ) {
  353.             robot[enemy].mine_status = 0 ;
  354.             }
  355.         }
  356.     /*
  357.      * Check to drop a mine.
  358.      */
  359.     else if( !kids_mode ) {
  360.         if( robot[enemy].dist < 175.0f ) {
  361.             robot[enemy].mine_x = player[enemy].x ;
  362.             robot[enemy].mine_y = player[enemy].y ;
  363.             robot[enemy].mine_status = 1 ;
  364.             }
  365.         }
  366. }
  367.  
  368.  
  369.  
  370. /*------------------------------------------------------------------------------
  371.  * Check if the enemy was hit by SELF.
  372.  *----------------------------------------------------------------------------*/
  373. static void check_enemy_hit(
  374.     unsigned int enemy
  375.     )
  376. {
  377.     int    kill_em = 0 ;
  378.  
  379.     if( IS_ALIVE( player[enemy] ) && player[SELF].hit_by_id == enemy ) {
  380.         post_new_message( 0, 0, "Killed %s.", player[enemy].name ) ;
  381.         player[SELF].hit_by_id = 0 ;
  382.         kill_em = MISSILE_STRIKE ;
  383.         if( player[SELF].hit_type == HIT_BY_MINE ) {
  384.             explosion_sfx( player[enemy].x - player[SELF].x,
  385.                            player[enemy].y - player[SELF].y,
  386.                            player[SELF].head ) ;
  387.             }
  388.         }
  389.  
  390.     if( player[enemy].x < -FIELD_SIZE ) {
  391.         player[enemy].x = -FIELD_SIZE ;
  392.         kill_em = VAPORIZE ;
  393.         }
  394.     if( player[enemy].x >  FIELD_SIZE ) {
  395.         player[enemy].x =  FIELD_SIZE ;
  396.         kill_em = VAPORIZE ;
  397.         }
  398.     if( player[enemy].y < -FIELD_SIZE ) {
  399.         player[enemy].y = -FIELD_SIZE ;
  400.         kill_em = VAPORIZE ;
  401.         }
  402.     if( player[enemy].y >  FIELD_SIZE ) {
  403.         player[enemy].y =  FIELD_SIZE ;
  404.         kill_em = VAPORIZE ;
  405.         }
  406.  
  407.     if( kill_em ) {
  408.         explode_enemy( enemy, kill_em ) ;
  409.         }
  410. }
  411.  
  412.  
  413.  
  414. /*------------------------------------------------------------------------------
  415.  * Explode the enemy.
  416.  *----------------------------------------------------------------------------*/
  417. static void explode_enemy(
  418.     unsigned int enemy,
  419.     int kill_type
  420.     )
  421. {
  422.     float                dx ;
  423.     float                dy ;
  424.  
  425.     /*
  426.      * Kill due to missile strike (explosion sfx will come from missile).
  427.      */
  428.     if( kill_type == MISSILE_STRIKE ) {
  429.         if( kids_mode ) {
  430.             switch( player[enemy].tank_type ) {
  431.                 case 0 :
  432.                     player[SELF].score += 10 ;
  433.                     break ;
  434.                 case 1 :
  435.                     player[SELF].score += 15 ;
  436.                     break ;
  437.                 case 3 :
  438.                     player[SELF].score += 20 ;
  439.                     break ;
  440.                 }
  441.             }
  442.         else {
  443.             switch( player[enemy].tank_type ) {
  444.                 case 0 :
  445.                     player[SELF].score += 50 ;
  446.                     break ;
  447.                 case 1 :
  448.                     player[SELF].score += 75 ;
  449.                     break ;
  450.                 case 3 :
  451.                     player[SELF].score += 100 ;
  452.                     break ;
  453.                 }
  454.             }
  455.         if( player[SELF].score >= new_man_score ) {
  456.             new_man_score += new_man_score_inc ;
  457.             lives++ ;
  458.             need_to_show_lives = TRUE ;
  459.             post_new_message( 0, 0, "Extra life awarded.  Next life at "
  460.                               "%d points.", new_man_score ) ;
  461.             sfx( SFX_FLAG_WON ) ;
  462.             }
  463.         }
  464.     /*
  465.      * Explode due to hitting boundary (no score).
  466.      */
  467.     else {
  468.         explosion_sfx( player[enemy].msl_x - player[SELF].x,
  469.                        player[enemy].msl_y - player[SELF].y,
  470.                        player[SELF].head ) ;
  471.         if( kill_type == VAPORIZE ) {
  472.             post_new_message( 0, 0, "%s was vaporized.",
  473.                     player[enemy].name ) ;
  474.             }
  475.         }
  476.  
  477.     /*
  478.      * Detonate mine if it was out there.
  479.      */
  480.     if( robot[enemy].mine_status ) {
  481.         dx = player[SELF].x - robot[enemy].mine_x ;
  482.         dy = player[SELF].y - robot[enemy].mine_y ;
  483.         /*
  484.          * Detonate mine if tank is within range.
  485.          */
  486.         if( dx * dx + dy * dy < DET_MINE_RADIUS * DET_MINE_RADIUS ) {
  487.             player[enemy].hit_type = HIT_BY_MINE ;
  488.             blow_up_self( enemy ) ;
  489.             }
  490.         robot[enemy].mine_status = 0 ;
  491.         }
  492.  
  493.     need_to_show_score = TRUE ;
  494.     vx[enemy] = vy[enemy] = 0.f ;
  495.     player[enemy].status = MAX_DEAD_STATUS ;
  496.     copy_time( &(robot[enemy].death_time), ¤t_time ) ;
  497. }
  498.  
  499.  
  500.  
  501. /*------------------------------------------------------------------------------
  502.  * Initialize stuff for solo version.
  503.  *----------------------------------------------------------------------------*/
  504. void solo_init( void )
  505. {
  506.     int                    i ;
  507.  
  508.     robot_logo_init() ;
  509.  
  510.     inc_level() ;
  511.  
  512.     if( kids_mode ) {
  513.         new_man_score_inc = 250 ;
  514.         }
  515.     else {
  516.         new_man_score_inc = 1000 ;
  517.         }
  518.     new_man_score = new_man_score_inc ;
  519.  
  520.     post_new_message( 0, 0, " " ) ;
  521.     if( practice_level ) {
  522.         post_new_message( 0, 0, "Practice level %u.", practice_level ) ;
  523.         }
  524.     else {
  525.         list_top_10() ;
  526.         }
  527.     post_new_message( 0, 0, " " ) ;
  528.     post_new_message( 0, 0, "Extra life at %d points.", new_man_score ) ;
  529.  
  530.     for( i = ENEMY ; i < number_players ; i++ ) {
  531.         place_enemy( i ) ;
  532.         }
  533. }
  534.  
  535.  
  536.  
  537. /*------------------------------------------------------------------------------
  538.  * Determine the enemy tank's movement.
  539.  *----------------------------------------------------------------------------*/
  540. static void enemy_move(
  541.     unsigned int enemy
  542.     )
  543. {
  544.     float            dist ;
  545.     float            dx ;
  546.     float            dy ;
  547.     float            dxm ;
  548.     float            dym ;
  549.     float            t ;
  550.     float            t_lead ;
  551.     float            ang ;
  552.     float            ga ;
  553.     float            dang ;
  554.     float            seen_angle ;
  555.     float            r ;
  556.     float            l ;
  557.     int                shoot ;
  558.     long            status ;
  559.     unsigned        seen ;
  560.  
  561.     dxm = player[enemy].x - player[SELF].msl_x ;
  562.     dym = player[enemy].y - player[SELF].msl_y ;
  563.     dx  = player[SELF].x  - player[enemy].x ;
  564.     dy  = player[SELF].y  - player[enemy].y ;
  565.     dist = robot[enemy].dist ;
  566.     t = dist / rm_vel ;
  567.     seen_angle = getangle( dy, dx ) ;
  568.     /*
  569.      * ang is the angle of SELF in enemy's field of vision.
  570.      */
  571.     ang = fmodf( 720.0f + seen_angle - player[enemy].head, 360.0f ) ;
  572.     /*
  573.      * seen_angle is the angle of enemy in self's field of vision.
  574.      */
  575.     seen_angle = fmodf( 720.0f - seen_angle + player[SELF].head, 360.0f ) ;
  576.     /*
  577.      * seen = 1 if in self's field of vision.
  578.      */
  579.     seen = ( seen_angle > 165.0f && seen_angle < 195.0f ) ;
  580.  
  581.     /*
  582.      * dang is delta angle to turn to be able to fire at SELF.
  583.      */
  584.     if( player[enemy].tank_type > 0 ) {
  585.         /*
  586.          * if not normal tank, find angle for firing allowing
  587.          * for lead.
  588.          */
  589.         t_lead = (float)( robot[enemy].lead_me ) * t / 3.f ;
  590.         ga = getangle( dy+t_lead*vy[SELF], dx+t_lead*vx[SELF] ) ;
  591.         dang = fmodf( 720.0f + ga - player[enemy].head, 360.0f ) ;
  592.         }
  593.     else {
  594.         /*
  595.          * if not super tank, find angle for firing based on
  596.          * where SELF is now.
  597.          */
  598.         dang = ang ;
  599.         }
  600.  
  601.     /*
  602.      * determine if missile headed at tank
  603.      */
  604.     if( player[SELF].msl_status && aimed_at_enemy( enemy ) ) {
  605.         /*
  606.          * missile is headed at tank
  607.          */
  608.         avoid_missile( enemy, rm_vel, dxm, dym, ang, dang, dist,
  609.                        &l, &r ) ;
  610.         }
  611.     else {
  612.         /*
  613.          * missile not headed at tank
  614.          */
  615.         prowl( enemy, dang, seen, dist, &l, &r ) ;
  616.         }
  617.  
  618.     turn_and_go( l, r, &(player[enemy].head), &vx[enemy],
  619.                  &vy[enemy]) ;
  620.  
  621.     if( !kids_mode && seen_angle > ( 180.0f - fire_ang ) &&
  622.         seen_angle < ( 180.0f + fire_ang ) ) {
  623.         if( ( shoot = clear_shot( enemy ) ) == 1 ) {
  624.             robot[enemy].can_wait = max_wait ;
  625.             copy_time( &(robot[enemy].wait_time), ¤t_time ) ;
  626.             }
  627.         }
  628.     else {
  629.         if( robot[enemy].can_wait > 0 ) {
  630.             status = max_wait - elapsed_sec_tenths( &(robot[enemy].wait_time),
  631.                                                     ¤t_time ) ;
  632.             if( status <= 0 ) {
  633.                 robot[enemy].can_wait = 0 ;
  634.                 }
  635.             else {
  636.                 robot[enemy].can_wait = status ;
  637.                 }
  638.             }
  639.         if( robot[enemy].can_wait ) {
  640.             shoot = 0 ;
  641.             }
  642.         else {
  643.             shoot = clear_shot( enemy ) ;
  644.             }
  645.         }
  646.     if( IS_ALIVE( player[enemy] ) && IS_ALIVE( player[SELF] )
  647.         && shoot && robot[enemy].enemy_loading == 0 &&
  648.         ( dang < 1.1f || dang > 358.9f ) ) {
  649.         enemy_fire( enemy ) ;
  650.         robot[enemy].lead_me = rand() % 4 ;
  651.         }
  652. }
  653.  
  654.  
  655.  
  656.  
  657. /*------------------------------------------------------------------------------
  658.  * Avoid missile.
  659.  *----------------------------------------------------------------------------*/
  660. static void avoid_missile(
  661.     unsigned int enemy,
  662.     float v,                    /* Velocity of missile. */
  663.     float dxm,                    /* Delta's of enemy - missile positions. */
  664.     float dym,
  665.     float ang,                    /* Angle of SELF from enemy's heading. */
  666.     float dang,                    /* Delta angle to firing heading. */
  667.     float dist,                    /* Distance to SELF. */
  668.     float *l,
  669.     float *r
  670.     )
  671. {
  672.     float            t ;
  673.     float            t0 ;
  674.     float            dot ;
  675.     float            vm ;
  676.     float            d ;                /* Distance to missile. */
  677.     float            max_turn ;        /* Maximum turning angle before impact. */
  678.     float            ue ;
  679.     float            ve ;
  680.     float            numer ;
  681.     float            denom ;
  682.  
  683.     d = hypotn( dxm, dym ) ;
  684.  
  685.     robot[enemy].avoiding = 1 ;
  686.     vm = vmax[player[enemy].tank_type] ;
  687.     /*
  688.      * Time 'til impact.
  689.      */
  690.     t = d / v ;
  691.  
  692.     dot = dxm * SINE( player[enemy].head ) +
  693.           dym * COSINE( player[enemy].head ) ;
  694.     /*
  695.      * v is the velocity perpendicular to the missile path
  696.      *
  697.      * v^2 = vm^2 * ( 1 - |heading . dist| / dist )
  698.      */
  699.     v = sqrtf( 1.0f - ( fabsf( dot ) / d ) ) * vm ;
  700.  
  701.     ue = vm *   SINE( player[enemy].head ) ;
  702.     ve = vm * COSINE( player[enemy].head ) ;
  703.     denom = ue * msl_vy[SELF] - ve * msl_vx[SELF] ;
  704.     numer = msl_vx[SELF] * dym - msl_vy[SELF] * dxm ;
  705.  
  706.     /*
  707.      * Only interested in sign of numer/denom.
  708.      */
  709.     t0 = ( ( numer < 0.0f ) ? -1.0f : 1.0f ) *
  710.          ( ( denom < 0.0f ) ? -1.0f : 1.0f ) ;
  711.  
  712.     /*
  713.      * If don't need to turn, simply go forward or backward.
  714.      */
  715.     if( v*t > MISLRMIN ) {
  716.         robot[enemy].turning = 0 ;
  717.         if( t0 < 0.f ) {
  718.             *l = vm ;
  719.             *r = vm ;
  720.             }
  721.         else {
  722.             *l = -vm ;
  723.             *r = -vm ;
  724.             }
  725.         }
  726.  
  727.     /*
  728.      * have to try to turn to avoid missile
  729.      */
  730.     else {
  731.         if( robot[enemy].turning < 5 ) {
  732.             if( !robot[enemy].turning )
  733.                 robot[enemy].flip = 0 ;
  734.             robot[enemy].turning += 1 ;
  735.             /*
  736.              * check to kamikaze
  737.              */
  738.             max_turn = t_factor * 2.f * vmax[player[enemy].tank_type] * t ;
  739.  
  740.             if( dist < 180.f && 0.1f * robot[enemy].enemy_loading < t && 
  741.                 ( dang < max_turn || dang > 360.0f - max_turn ) ) {
  742.                 if( dang > 1.0f && dang < 180.0f ) {
  743.                     robot[enemy].turning_l =  vm ;
  744.                     robot[enemy].turning_r = -vm ;
  745.                     }
  746.                 else if( dang >= 180.0f && dang < 359.0f ) {
  747.                     robot[enemy].turning_l = -vm ;
  748.                     robot[enemy].turning_r =  vm ;
  749.                     }
  750.                 else {
  751.                     robot[enemy].turning_l = robot[enemy].turning_r = -vm ;
  752.                     }
  753.                 }
  754.             /*
  755.              * SELF on left side.
  756.              */
  757.             else if( ang > 180.0f ) {
  758.                 if( t0 < 0.f ) {
  759.                     robot[enemy].turning_r = 0.f ;
  760.                     robot[enemy].turning_l = vm ;
  761.                     }
  762.                 else {
  763.                     robot[enemy].turning_r = -vm ;
  764.                     robot[enemy].turning_l = 0.f ;
  765.                     }
  766.                 }
  767.             /*
  768.              * SELF on right side.
  769.              */
  770.             else {
  771.                 if( t0 < 0.f ) {
  772.                     robot[enemy].turning_r = vm ;
  773.                     robot[enemy].turning_l = 0.f ;
  774.                     }
  775.                 else {
  776.                     robot[enemy].turning_r = 0.f ;
  777.                     robot[enemy].turning_l = vm ;
  778.                     }
  779.                 }
  780.             }
  781.         *l = robot[enemy].turning_l ;
  782.         *r = robot[enemy].turning_r ;
  783.         }
  784. }
  785.  
  786.  
  787.  
  788. /*------------------------------------------------------------------------------
  789.  * Prowl (controls movement when not in danger of being hit).
  790.  *----------------------------------------------------------------------------*/
  791. static void prowl(
  792.     unsigned int enemy,
  793.     float dang,
  794.     unsigned seen,
  795.     float dist,
  796.     float *l,
  797.     float *r
  798.     )
  799. {
  800.     float            t_a ;
  801.     unsigned        retreat ;
  802.     unsigned        fire_order ;
  803.     float            vm ;
  804.  
  805.     robot[enemy].turning = 0 ;
  806.  
  807.     if( !seen || ( robot[enemy].enemy_loading <= loading_missile ) )
  808.         retreat = 0 ;
  809.     else
  810.         retreat = 1 ;
  811.  
  812.     vm = vmax[player[enemy].tank_type] ;
  813.  
  814.     switch( player[enemy].tank_type ) {
  815.         case 0 :
  816.             /*
  817.              * if not tank not at target angle, turn either right or left
  818.              */
  819.             if( dang > 1.0f && dang < 180.0f ) {
  820.                 *l =  vm ;
  821.                 *r = -vm ;
  822.                 }
  823.             else if( dang >= 180.0f && dang < 359.0f ) {
  824.                 *l = -vm ;
  825.                 *r =  vm ;
  826.                 }
  827.             else {
  828.                 /*
  829.                  * already facing tank, either approach or retreat
  830.                  */
  831.                 if( !retreat ) {
  832.                     if( dist > 100.f )
  833.                         *l = *r = vm ;
  834.                     else
  835.                         *l = *r = 0.f ;
  836.                     }
  837.                 else {
  838.                     if( dist < ( kids_mode ) ? 300.f : 800.f )
  839.                         *l = *r = -vm ;
  840.                     else
  841.                         *l = *r = 0.f ;
  842.                     }
  843.                 }
  844.             break ;
  845.         case 1 :
  846.         case 3 :
  847.             if( robot[enemy].enemy_loading < loading_missile )
  848.                 fire_order = 2 ;
  849.             else if( robot[enemy].enemy_loading > loading_missile )
  850.                 fire_order = 1 ;
  851.             else
  852.                 fire_order = 0 ;
  853.  
  854.             if( robot[enemy].flip == 0 ) {
  855.                 robot[enemy].flip = 1 ;
  856.                 robot[enemy].target_angle =
  857.                     fmodf( 720.0f - robot[enemy].target_angle, 360.0f ) ;
  858.                 }
  859.             t_a = ( dist > 150.f && fire_order != 2 && seen ) ?
  860.                     robot[enemy].target_angle : 0 ;
  861.             dang = fmodf( 720.0f + dang + t_a, 360.0f ) ;
  862.  
  863.             if( dang < 1.0f || dang > 359.0f ) {
  864.                 /*
  865.                  *  facing tank directly
  866.                  */
  867.                 robot[enemy].avoiding = robot[enemy].flip = 0 ;
  868.                 if( dist > 500.f )
  869.                     *l = *r = vm ;
  870.                 else if( dist < 130.f )
  871.                     *l = *r = -vm ;
  872.                 else if( dist < 150.f )
  873.                     *l = *r = 0.f ;
  874.                 else
  875.                     *l = *r = ( fire_order != 1 ) ? vm : 0.f ;
  876.                 }
  877.             else if( dang < 50.0f || dang > 310.0f ) {
  878.                 /*
  879.                  *  tank in viewscreen
  880.                  */
  881.                 if( dist > 150.f ) {
  882.                     if( dang > 180.0f ) {
  883.                         *r = vm ;
  884.                         *l = ( robot[enemy].avoiding ) ? -vm : 0.f ;
  885.                         }
  886.                     else {
  887.                         *r = ( robot[enemy].avoiding ) ? -vm : 0.f ;
  888.                         *l = vm ;
  889.                         }
  890.                     }
  891.                 else {
  892.                     if( dang > 180.0f )
  893.                         *l = -( *r = vm ) ;
  894.                     else
  895.                         *r = -( *l = vm ) ;
  896.                     }
  897.                 }
  898.             else {
  899.                 /*
  900.                  *  tank not in viewscreen
  901.                  */
  902.                 if( dang > 180.0f )
  903.                     *l = -( *r = vm ) ;
  904.                 else
  905.                     *r = -( *l = vm ) ;
  906.                 }
  907.             break ;
  908.         }
  909. }
  910.  
  911.  
  912.  
  913. /*------------------------------------------------------------------------------
  914.  * Increment the playing level.
  915.  *----------------------------------------------------------------------------*/
  916. static void inc_level( void )
  917. {
  918.     if( practice_level == 0 )
  919.         level++ ;
  920.  
  921.     number_players = ( ( ( level - 1 ) / 3 ) + 2 ) ;
  922.     if( number_players > MAX_ENEMY + 1 ) {
  923.         number_players = MAX_ENEMY + 1 ;
  924.         }
  925.  
  926.     need_to_show_level = TRUE ;
  927.     need_to_show_players = TRUE ;
  928.  
  929.     tanks_per_level = 1 + ( 3 * ( level + 1 ) ) / 4 ;
  930.  
  931.     if( kids_mode ) {
  932.         max_wait = 600 - (level - 1) * 100 ;
  933.         if( max_wait < 50 )
  934.             max_wait = 50 ;
  935.         }
  936.     else {
  937.         max_wait = 150 - (level - 1) * 25 ;
  938.         if( max_wait < 20 )
  939.             max_wait = 20 ;
  940.         }
  941.  
  942.     zig_zag_angle = ( level - 1 ) * 3.0f ;
  943.     if( zig_zag_angle > 30.0f )
  944.         zig_zag_angle = 30.0f ;
  945.  
  946.     if( kids_mode ) {
  947.         fire_ang = ( level - 1 ) * 2.0f + 30.0f ;
  948.         }
  949.     else {
  950.         fire_ang = ( level - 1 ) * 5.0f + 30.0f ;
  951.         }
  952.     if( fire_ang > 180.0f )
  953.         fire_ang = 180.0f ;
  954.  
  955.     if( level < 5 ) {
  956.         super_tank_prob = ( level -  1 ) * 20 ;
  957.         runner_tank_prob = 0 ;
  958.         }
  959.     else {
  960.         super_tank_prob = 60 + ( level -  4 ) * 10 ;
  961.         runner_tank_prob = ( level - 5 ) * 20 ;
  962.         }
  963.     if( super_tank_prob > 100 ) {
  964.         super_tank_prob = 100 ;
  965.         }
  966.     if( runner_tank_prob > 100 ) {
  967.         runner_tank_prob = 100 ;
  968.         }
  969. }
  970.  
  971.  
  972.  
  973. /*------------------------------------------------------------------------------
  974.  * Cause enemy tanks to fire.
  975.  *----------------------------------------------------------------------------*/
  976. static void enemy_fire(
  977.     unsigned int enemy
  978.     )
  979. {
  980.     robot[enemy].enemy_loading = player[enemy].msl_status = RM_DURATN ;
  981.  
  982.     player[enemy].msl_head = player[enemy].head ;
  983.  
  984.     turn_and_go( rm_vel, rm_vel, &(player[enemy].msl_head),
  985.                  &msl_vx[enemy], &msl_vy[enemy] ) ;
  986.  
  987.     last_msl_x[enemy] = player[enemy].msl_x =
  988.                             player[enemy].x + 0.25f * msl_vx[enemy] ;
  989.     last_msl_y[enemy] = player[enemy].msl_y =
  990.                             player[enemy].y + 0.25f * msl_vy[enemy] ;
  991.  
  992.     copy_time( &(robot[enemy].msl_launch), ¤t_time ) ;
  993. }
  994.  
  995.  
  996.  
  997. /*------------------------------------------------------------------------------
  998.  * Place enemy tanks on the field.
  999.  *----------------------------------------------------------------------------*/
  1000. static void place_enemy(
  1001.     int i
  1002.     )
  1003. {
  1004.     int                    j ;
  1005.     int                    k ;
  1006.     float                r ;
  1007.     float                ang ;
  1008.     int                    rand_val ;
  1009.  
  1010.     do{
  1011.         j = 0 ;
  1012.         r = 100.f + (float)( rand() & 0x1ff ) ;
  1013.         ang = (float)( rand() % 360 ) ;
  1014.         player[i].x = player[SELF].x + r * COSINE(ang) ;
  1015.         player[i].y = player[SELF].y + r * SINE(ang) ;
  1016.         r = 0.9f * FIELD_SIZE ;
  1017.         if( player[i].x < -r ) player[i].x = -r ;
  1018.         if( player[i].y < -r ) player[i].y = -r ;
  1019.         if( player[i].x >  r ) player[i].x =  r ;
  1020.         if( player[i].y >  r ) player[i].y =  r ;
  1021.         for( k = 0 ; k < TOTALOBSTS ; k++ ) {
  1022.             if( square( player[i].x - obst_x[k], player[i].y - obst_y[k] )
  1023.                         < 1600.f) {
  1024.                 j = 1 ;
  1025.                 }
  1026.             }
  1027.         for( k = ENEMY ; k < number_players ; k++ ) {
  1028.             if( k != i &&
  1029.                 square( player[i].x - player[k].x, player[i].y - player[k].y )
  1030.                         < 1600.f) {
  1031.                 j = 1 ;
  1032.                 }
  1033.             }
  1034.         } while( j != 0 );
  1035.     player[i].id = i ;
  1036.     player[i].head = ( float )( rand() % 3600 ) / 10.f ;
  1037.     player[i].status = ALIVE_STATUS ;
  1038.     player[i].team = ( ( level - 1 ) % N_TEAMS ) ;
  1039.     player[i].msl_status = 0 ;
  1040.     robot[i].mine_status = 0 ;
  1041.  
  1042.     rand_val = rand() % 100 ;
  1043.  
  1044.     if( rand_val < runner_tank_prob ) {
  1045.         player[i].tank_type = 3 ;
  1046.         strcpy( player[i].name, next_runner_name() ) ;
  1047.         }
  1048.     else if( rand_val < super_tank_prob ) {
  1049.         player[i].tank_type = 1 ;
  1050.         strcpy( player[i].name, next_super_name() ) ;
  1051.         }
  1052.     else {
  1053.         player[i].tank_type = 0 ;
  1054.         strcpy( player[i].name, next_regular_name() ) ;
  1055.         }
  1056.  
  1057.     robot[i].enemy_loading = RM_DURATN / 2 ;
  1058.     set_time( &(robot[i].msl_launch), ¤t_time, 0,
  1059.                 robot[i].enemy_loading * 100000 ) ;
  1060.     robot[i].can_wait = max_wait ;
  1061.     copy_time( &(robot[i].wait_time), ¤t_time ) ;
  1062.     robot[i].lead_me = (rand() % 4 ) ;
  1063.     robot[i].flip = 20 ;
  1064.     robot[i].target_angle = zig_zag_angle ;
  1065.  
  1066.     if( logo_count > 0 ) {
  1067.         tank_logo[i] = next_logo( &(tank_logo_side[i]) ) ;
  1068.         }
  1069. }
  1070.  
  1071.  
  1072.  
  1073. /*------------------------------------------------------------------------------
  1074.  * Clean up when enemy tank is destroyed.
  1075.  *----------------------------------------------------------------------------*/
  1076. static void bury_enemy(
  1077.     unsigned int enemy
  1078.     )
  1079. {
  1080.     unsigned int        i ;
  1081.  
  1082.     if( practice_level == 0 ) {
  1083.         tanks_per_level--;
  1084.         }
  1085.  
  1086.     /*
  1087.      * All tanks on this level destroyed.  Go to a new level.
  1088.      */
  1089.     if( !tanks_per_level ) {
  1090.         inc_level() ;
  1091.         for( i = ENEMY ; i < number_players ; i++ ) {
  1092.             place_enemy(i) ;
  1093.             robot[i].flip = 20 ;
  1094.             }
  1095.         }
  1096.     /*
  1097.      * No more tanks to appear on this level (nearing the end).
  1098.      */
  1099.     else if( tanks_per_level < number_players-1 ) {
  1100.         if( number_players > 2 ) {
  1101.             if( enemy < number_players-1 ) {
  1102.                 copy_player( enemy, &(player[number_players-1]) ) ;
  1103.                 player[enemy].id = enemy ;
  1104.                 vx[enemy] = vx[number_players-1] ;
  1105.                 vy[enemy] = vy[number_players-1] ;
  1106.                 msl_vx[enemy] = msl_vx[number_players-1] ;
  1107.                 msl_vy[enemy] = msl_vy[number_players-1] ;
  1108.                 tank_logo[enemy] = tank_logo[number_players-1] ;
  1109.                 tank_logo_side[enemy] = tank_logo_side[number_players-1] ;
  1110.                 bcopy( &(robot[number_players-1]), &(robot[enemy]),
  1111.                         sizeof( robot[enemy] ) ) ;
  1112.                 }
  1113.             player[number_players-1].status = MIN_DEAD_STATUS ;
  1114.             player[number_players-1].msl_status = 0 ;
  1115.             }
  1116.         else {
  1117.             place_enemy(enemy) ;
  1118.             }
  1119.         number_players-- ;
  1120.         reset_viewing_order() ;
  1121.         need_to_show_players = TRUE;
  1122.         }
  1123.     /*
  1124.      * Have more tanks to appear.
  1125.      */
  1126.     else if( enemy < number_players ) {
  1127.         place_enemy(enemy) ;
  1128.         }
  1129.     else {
  1130.         player[enemy].status = -1 ;
  1131.         }
  1132. }
  1133.  
  1134.  
  1135.  
  1136. char *top_file = "bz.top" ;
  1137. #define    MAX_SCORES    10
  1138.  
  1139.  
  1140.  
  1141. /*------------------------------------------------------------------------------
  1142.  * Return the name of the topscore file.
  1143.  *----------------------------------------------------------------------------*/
  1144. static char *get_topscore_file( void )
  1145. {
  1146.     char        *t_dir ;
  1147.     char        *filename ;
  1148.     unsigned    i ;
  1149.     unsigned    j ;
  1150.  
  1151.     if( ( t_dir = getenv( "BZ_SCORE_DIR" ) ) == NULL &&
  1152.         ( t_dir = getenv( "BZ_DIR" ) ) == NULL ) {
  1153.         t_dir = DATA_DIR ;
  1154.         }
  1155.  
  1156.     i = strlen( t_dir ) ;
  1157.     j = 0 ;
  1158.     if( i > 0 && t_dir[i-1] != '/' ) {
  1159.         j = 1 ;
  1160.         }
  1161.  
  1162.     i += strlen( top_file ) + 1 ;
  1163.  
  1164.     if( ( filename = malloc( i + j ) ) == NULL ) {
  1165.         return( NULL ) ;
  1166.         }
  1167.  
  1168.     if( j ) {
  1169.         sprintf( filename, "%s/%s", t_dir, top_file ) ;
  1170.         }
  1171.     else {
  1172.         sprintf( filename, "%s%s", t_dir, top_file ) ;
  1173.         }
  1174.  
  1175.     return( filename ) ;
  1176. }
  1177.  
  1178.  
  1179.  
  1180. /*------------------------------------------------------------------------------
  1181.  * Load scores and names from top 10 file.
  1182.  *----------------------------------------------------------------------------*/
  1183. static int load_top_10_stuff(
  1184.     FILE *f,
  1185.     char names[][NAMELEN],
  1186.     unsigned int topscore[],
  1187.     int max_scores
  1188.     )
  1189. {
  1190.     unsigned    i ;
  1191.     unsigned    n ;
  1192.     char        *c ;
  1193.     char        line[256] ;
  1194.  
  1195.     for( i = 0 ; i < max_scores ; i++ ) {
  1196.         if( fgets( line, sizeof( line ), f ) == NULL ) {
  1197.             topscore[i] = 0 ;
  1198.             names[i][0] = '\0' ;
  1199.             return( i ) ;
  1200.             }
  1201.         else {
  1202.             if( ( c = strrchr( line, ' ' ) ) == NULL ) {
  1203.                 topscore[i] = 0 ;
  1204.                 names[i][0] = '\0' ;
  1205.                 return( -i ) ;
  1206.                 }
  1207.             else {
  1208.                 topscore[i] = atoi( c ) ;
  1209.                 while( *c == ' ' && c != line ) {
  1210.                     *(c--) = '\0' ;
  1211.                     }
  1212.                 strncpy( names[i], line, NAMELEN ) ;
  1213.                 }
  1214.             }
  1215.         }
  1216.     return( i ) ;
  1217. }
  1218.  
  1219.  
  1220.  
  1221. /*------------------------------------------------------------------------------
  1222.  * Update the top scores.
  1223.  *----------------------------------------------------------------------------*/
  1224. static void do_top_10( void )
  1225. {
  1226.     FILE                *f ;
  1227.     unsigned            i ;
  1228.     unsigned            j ;
  1229.     unsigned            n ;
  1230.     char                *filename ;
  1231.     char                names[MAX_SCORES+1][NAMELEN] ;
  1232.     unsigned            topscore[MAX_SCORES] ;
  1233.  
  1234.     if( ( filename = get_topscore_file() ) == NULL ) {
  1235.         perror( "do_top_10" ) ;
  1236.         return ;
  1237.         }
  1238.  
  1239.     strcpy( names[MAX_SCORES], player_name ) ;
  1240.  
  1241.     if( ( f = fopen( filename, "r" ) ) != NULL ) {
  1242.         if( ( i = load_top_10_stuff( f, names, topscore, MAX_SCORES ) ) < 0 ) {
  1243.             printf( "top score file, %s, trashed\n", filename ) ;
  1244.             i = -i ;
  1245.             }
  1246.         }
  1247.     else {
  1248.         i = 0 ;
  1249.         fprintf( stderr, "Can't read top scores file %s\n\n", filename ) ;
  1250.         }
  1251.  
  1252.     fclose( f ) ;
  1253.     n = i ;
  1254.     for( i = 0 ; i < n && topscore[i] >= player[SELF].score ; )
  1255.         i++ ;
  1256.  
  1257.     if( !player[SELF].score || practice_level ) {
  1258.         i = j = MAX_SCORES + 1 ;
  1259.         }
  1260.     if( i < MAX_SCORES ) {
  1261.         if( n > MAX_SCORES - 1 )
  1262.             n = MAX_SCORES - 1 ;
  1263.         for( j = n ; j > i ; j-- ) {
  1264.             topscore[j] = topscore[j-1];
  1265.             strcpy( names[j], names[j-1] ) ;
  1266.             }
  1267.         n = ( n < MAX_SCORES ) ? n+1 : MAX_SCORES ;
  1268.         strcpy( names[i], names[MAX_SCORES] ) ;
  1269.         topscore[i] = player[SELF].score ;
  1270.         j = i ;
  1271.         if( ( f = fopen( filename, "w" ) ) != NULL ) {
  1272.             for( i = 0 ; i < n ; i++) {
  1273.                 fprintf( f, "%s %u\n", names[i], topscore[i] ) ;
  1274.                 }
  1275.             fclose(f);
  1276.             }
  1277.         else {
  1278.             fprintf( stderr, "Unable to open %s\n", filename ) ;
  1279.             }
  1280.         }
  1281.     else {
  1282.         j = MAX_SCORES + 1 ;
  1283.         }
  1284.  
  1285.     printf( "\n\t    TOP SCORES\n" ) ;
  1286.     for( i = 0 ; i < n ; i++ ) {
  1287.         printf( "    %2d  %-12s  %6u %c\n", (i+1), names[i], topscore[i],
  1288.                 ( ( i == j ) ? '<' : ' ' ) ) ;
  1289.         }
  1290.     if( j >= n ) {
  1291.         if( practice_level ) {
  1292.             printf( "\n    Your score:       %6u (at practice level %u)\n",
  1293.                     player[SELF].score, practice_level ) ;
  1294.             }
  1295.         else {
  1296.             printf( "\n    Your score:       %6u\n", player[SELF].score ) ;
  1297.             }
  1298.         }
  1299.     printf( "\n\n" ) ;
  1300.  
  1301.     free( filename ) ;
  1302. }
  1303.  
  1304.  
  1305.  
  1306. /*------------------------------------------------------------------------------
  1307.  * List the top scores to the message window.
  1308.  *----------------------------------------------------------------------------*/
  1309. static void list_top_10( void )
  1310. {
  1311.     FILE        *f ;
  1312.     unsigned    i ;
  1313.     unsigned    n ;
  1314.     char        names[MAX_SCORES+1][NAMELEN] ;
  1315.     char        *filename ;
  1316.     unsigned    topscore[MAX_SCORES] ;
  1317.  
  1318.     if( ( filename = get_topscore_file() ) == NULL ) {
  1319.         perror( "list_top_10" ) ;
  1320.         return ;
  1321.         }
  1322.  
  1323.     if( ( f = fopen( filename, "r" ) ) != NULL ) {
  1324.         if( ( n = load_top_10_stuff( f, names, topscore, MAX_SCORES ) ) < 0 ) {
  1325.             n = -n ;
  1326.             }
  1327.         for( i = 0 ; i < n ; i++ ) {
  1328.             if( i == 0 ) {
  1329.                 post_new_message( 0, 0, "       TOP SCORES" ) ;
  1330.                 }
  1331.             post_new_message( 0, 0, "%2d  %-12s  %6u", i+1, names[i],
  1332.                               topscore[i] ) ;
  1333.             }
  1334.         fclose( f ) ;
  1335.         }
  1336.  
  1337.     free( filename ) ;
  1338. }
  1339.  
  1340.  
  1341.  
  1342. /*------------------------------------------------------------------------------
  1343.  * Determine if missile is on line with enemy tank.
  1344.  *----------------------------------------------------------------------------*/
  1345. static int aimed_at_enemy(
  1346.     unsigned int enemy
  1347.     )
  1348. {
  1349.     float            self[2][2] ;
  1350.     float            obstacle[4][2] ;
  1351.  
  1352.     locate_tank_corner_points( enemy, obstacle ) ;
  1353.     self[0][0] = player[SELF].msl_x ;
  1354.     self[0][1] = player[SELF].msl_y ;
  1355.     self[1][0] = player[SELF].msl_x + RM_DURATN * msl_vx[SELF] ;
  1356.     self[1][1] = player[SELF].msl_y + RM_DURATN * msl_vy[SELF] ;
  1357.  
  1358.     return( check_line_to_rect( self, obstacle, 0 )  ) ;
  1359. }
  1360.  
  1361.  
  1362.  
  1363. /*------------------------------------------------------------------------------
  1364.  * Decrement life count.
  1365.  *----------------------------------------------------------------------------*/
  1366. void adjust_lives( void )
  1367. {
  1368.     if( practice_level == 0 && --lives < 1 ) {
  1369.         do_top_10() ;
  1370.         end_program( 0 ) ;
  1371.         }
  1372. }
  1373.  
  1374.  
  1375.  
  1376. /*------------------------------------------------------------------------------
  1377.  * Check to see if an enemy tank has a clear shot at self.
  1378.  *----------------------------------------------------------------------------*/
  1379. static int clear_shot(
  1380.     unsigned int enemy
  1381.     )
  1382. {
  1383.     int                obst ;
  1384.     float            path[2][2] ;
  1385.     float            obstacle[4][2] ;
  1386.  
  1387.     path[0][0] = player[enemy].x ;
  1388.     path[0][1] = player[enemy].y ;
  1389.     path[1][0] = player[SELF].x ;
  1390.     path[1][1] = player[SELF].y ;
  1391.  
  1392.     for( obst = CONES ; obst < CUBES ; obst++ ) {
  1393.         locate_obstacle_corner_points( obst, obstacle, CONE_COLLISION_INSET ) ;
  1394.         if( check_line_to_rect( path, obstacle, 0 ) )
  1395.             return( 0 ) ;
  1396.         }
  1397.  
  1398.     for( obst = CUBES ; obst < TOTALOBSTS ; obst++ ) {
  1399.         locate_obstacle_corner_points( obst, obstacle, 1.0f ) ;
  1400.         if( check_line_to_rect( path, obstacle, 0 ) )
  1401.             return( 0 ) ;
  1402.         }
  1403.  
  1404.     for( obst = ENEMY ; obst < number_players ; obst++ ) {
  1405.         if( obst != enemy ) {
  1406.             locate_tank_corner_points( obst, obstacle ) ;
  1407.             if( check_line_to_rect( path, obstacle, 0 ) )
  1408.                 return( 0 ) ;
  1409.             }
  1410.         }
  1411.  
  1412.     return( 1 ) ;
  1413. }
  1414.  
  1415.  
  1416.  
  1417. static char    *regular_names[] = {
  1418.         "Joe",         "Fred",        "Mary",        "Elmer",
  1419.         "John",        "Chuck",       "Gary",        "Wally",
  1420.         "Eddie",       "Tim",         "Baxter",      "Bob",
  1421.         "Betty",       "Poindexter",  "Sue",         "Winona"
  1422.         } ;
  1423.  
  1424. static char *super_names[] = {
  1425.         "Nitro",       "Butch",       "Spike",       "Animal",
  1426.         "Booger",      "Slash",       "Deadeye",     "Bullet",
  1427.         } ;
  1428.  
  1429. static char *runner_names[] = {
  1430.         "Blaze",       "Speedy",      "Lightning",   "Smoke",
  1431.         "Streaker",    "Flash",       "Quicksilver", "Blur",
  1432.         } ;
  1433.  
  1434.  
  1435. /*------------------------------------------------------------------------------
  1436.  * Unique random name retrieval kernel.
  1437.  *----------------------------------------------------------------------------*/
  1438. static char *next_name(
  1439.     int i,
  1440.     char *names[]
  1441.     )
  1442. {
  1443.     int            n ;
  1444.     char        *tmp ;
  1445.  
  1446.     n = rand() % i ;
  1447.  
  1448.     tmp = names[i-1] ;
  1449.     names[i-1] = names[n] ;
  1450.     names[n] = tmp ;
  1451.  
  1452.     return( names[i-1] ) ;
  1453. }
  1454.  
  1455.  
  1456.  
  1457. /*------------------------------------------------------------------------------
  1458.  * Return the name of the next regular tank.
  1459.  *----------------------------------------------------------------------------*/
  1460. static char *next_regular_name( void )
  1461. {
  1462.     static int    i = -1 ;
  1463.  
  1464.     if( i < 1 ) {
  1465.         i = sizeof( regular_names ) / sizeof( regular_names[0] ) ;
  1466.         }
  1467.  
  1468.     return( next_name( i--, regular_names ) ) ;
  1469. }
  1470.  
  1471.  
  1472. /*------------------------------------------------------------------------------
  1473.  * Return the name of the next super tank.
  1474.  *----------------------------------------------------------------------------*/
  1475. static char *next_super_name( void )
  1476. {
  1477.     static int    i = -1 ;
  1478.  
  1479.     if( i < 1 ) {
  1480.         i = sizeof( super_names ) / sizeof( super_names[0] ) ;
  1481.         }
  1482.  
  1483.     return( next_name( i--, super_names ) ) ;
  1484. }
  1485.  
  1486.  
  1487.  
  1488. /*------------------------------------------------------------------------------
  1489.  * Return the name of the next runner tank.
  1490.  *----------------------------------------------------------------------------*/
  1491. static char *next_runner_name( void )
  1492. {
  1493.     static int    i = -1 ;
  1494.  
  1495.     if( i < 1 ) {
  1496.         i = sizeof( runner_names ) / sizeof( runner_names[0] ) ;
  1497.         }
  1498.  
  1499.     return( next_name( i--, runner_names ) ) ;
  1500. }
  1501.  
  1502.  
  1503.  
  1504. /*------------------------------------------------------------------------------
  1505.  * Start pause.
  1506.  *----------------------------------------------------------------------------*/
  1507. void start_pause( void )
  1508. {
  1509.     start_time( &pause_begin ) ;
  1510.     reminders = 1 ;
  1511. }
  1512.  
  1513.  
  1514.  
  1515. /*------------------------------------------------------------------------------
  1516.  * End pause (update all times).
  1517.  *----------------------------------------------------------------------------*/
  1518. void end_pause( void )
  1519. {
  1520.     int                        i ;
  1521.     long                    secs ;
  1522.     long                    usecs ;
  1523.  
  1524.     (void) end_time( &pause_begin, &pause_end ) ;
  1525.  
  1526.     secs = pause_end.tv_sec - pause_begin.tv_sec ;
  1527.     usecs = pause_end.tv_usec - pause_begin.tv_usec ;
  1528.  
  1529.     for( i = ENEMY ; i < number_players ; i++ ) {
  1530.         add_to_time( &(robot[i].msl_launch), secs, usecs ) ;
  1531.         add_to_time( &(robot[i].msl_explode), secs, usecs ) ;
  1532.         add_to_time( &(robot[i].death_time), secs, usecs ) ;
  1533.         add_to_time( &(robot[i].wait_time), secs, usecs ) ;
  1534.         }
  1535.  
  1536.     add_to_time( &missile_launch_time, secs, usecs ) ;
  1537.     add_to_time( &missile_explode_time, secs, usecs ) ;
  1538.     add_to_time( &time_of_death, secs, usecs ) ;
  1539.     add_to_time( &hit_time, secs, usecs ) ;
  1540. }
  1541.  
  1542.  
  1543.  
  1544. /*------------------------------------------------------------------------------
  1545.  * Check pause (see if a specified amount of time has passed).
  1546.  *----------------------------------------------------------------------------*/
  1547. int check_pause( void )
  1548. {
  1549.     float    et ;
  1550.  
  1551.     et = end_time( &pause_begin, NULL ) ;
  1552.  
  1553.     if( et > reminders * REMINDER_DELAY ) {
  1554.         reminders++ ;
  1555.         return( 1 ) ;
  1556.         }
  1557.  
  1558.     return( 0 ) ;
  1559. }
  1560.  
  1561.  
  1562.  
  1563. /*------------------------------------------------------------------------------
  1564.  * Initialize robot logos.
  1565.  *----------------------------------------------------------------------------*/
  1566. static void robot_logo_init( void )
  1567. {
  1568.     int                i ;
  1569.     FILE            *f ;
  1570.     char            *c ;
  1571.     char            logo_file[MAXPATHLEN] ;
  1572.     unsigned char    logo_buf[LOGO_SIZE] ;
  1573.     char            *cmd ;
  1574.  
  1575.     if( ( c = getenv( "BZ_LOGO_DIR" ) ) == NULL &&
  1576.         ( c = getenv( "BZ_DIR" ) ) == NULL ) {
  1577.         c = DATA_DIR ;
  1578.         }
  1579.  
  1580.     i = strlen( c ) + 23 ;
  1581.     if( ( cmd = malloc( i ) ) == NULL ) {
  1582.         perror( "robot_logo_init" ) ;
  1583.         free( cmd ) ;
  1584.         return ;
  1585.         }
  1586.  
  1587.     sprintf( cmd, "/bin/ls %s/*.bzl | wc -l", c ) ;
  1588.  
  1589.     if( ( f = popen( cmd, "r" ) ) == NULL ) {
  1590.         logo_count = 0 ;
  1591.         free( cmd ) ;
  1592.         return ;
  1593.         }
  1594.     fscanf( f, "%d", &logo_count ) ;
  1595.     pclose( f ) ;
  1596.     if( logo_count <= 0 ) {
  1597.         free( cmd ) ;
  1598.         return ;
  1599.         }
  1600.  
  1601.     if( ( c = strrchr( cmd, '|' ) ) != NULL ) {
  1602.         *c = '\0' ;
  1603.         }
  1604.  
  1605.     if( ( f = popen( cmd, "r" ) ) == NULL ) {
  1606.         logo_count = 0 ;
  1607.         free( cmd ) ;
  1608.         return ;
  1609.         }
  1610.  
  1611.     free( cmd ) ;
  1612.  
  1613.     robot_logo = (GL_Object *)malloc( logo_count * sizeof(GL_Object) ) ;
  1614.     robot_logo_side = (int *)malloc( logo_count * sizeof(int) ) ;
  1615.     if( robot_logo == NULL || robot_logo_side == NULL ) {
  1616.         fprintf( stderr, "trying to allocate space for %ld logos...\n",
  1617.                  logo_count ) ;
  1618.         perror( "robot_logo_init" ) ;
  1619.         pclose( f ) ;
  1620.         if( robot_logo != NULL )
  1621.             free( robot_logo ) ;
  1622.         if( robot_logo_side != NULL )
  1623.             free( robot_logo_side ) ;
  1624.         return ;
  1625.         }
  1626.  
  1627.     for( i = 0 ; i < logo_count ; i++ ) {
  1628.         if( fgets( logo_file, MAXPATHLEN, f ) == NULL ) {
  1629.             break ;
  1630.             }
  1631.         /*
  1632.          * Remove trailing line feed if present.
  1633.          */
  1634.         if( ( c = strrchr( logo_file, '\n' ) ) != NULL ) {
  1635.             *c = '\0' ;
  1636.             }
  1637.  
  1638.         if( ( robot_logo[i] = read_logo( logo_file, logo_buf, LOGO_SIZE, 1,
  1639.                                          &(robot_logo_side[i]) ) ) == 0 ) {
  1640.             break ;
  1641.             }
  1642.         }
  1643.  
  1644.     logo_count = i ;
  1645. }
  1646.  
  1647.  
  1648.  
  1649. /*------------------------------------------------------------------------------
  1650.  * Unique random logo retrieval routine.
  1651.  *----------------------------------------------------------------------------*/
  1652. static GL_Object next_logo(
  1653.     int    *logo_side
  1654.     )
  1655. {
  1656.     int            n ;
  1657.     GL_Object    logo_tmp ;
  1658.     int            logo_side_tmp ;
  1659.     static int    i = -1 ;
  1660.  
  1661.     if( i < 1 ) {
  1662.         i = logo_count ;
  1663.         }
  1664.  
  1665.     n = rand() % i ;
  1666.  
  1667.     logo_tmp        = robot_logo[i-1] ;
  1668.     robot_logo[i-1] = robot_logo[n] ;
  1669.     robot_logo[n]   = logo_tmp ;
  1670.  
  1671.     logo_side_tmp        = robot_logo_side[i-1] ;
  1672.     robot_logo_side[i-1] = robot_logo_side[n] ;
  1673.     robot_logo_side[n]   = logo_side_tmp ;
  1674.  
  1675.     *logo_side = robot_logo_side[i-1] ;
  1676.  
  1677.     return( robot_logo[--i] ) ;
  1678. }
  1679.